﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Windows.Forms;

using SetPath;

namespace EasyPath
{
    public partial class EasyPathControl : UserControl
    {
        private readonly bool _isRunningAsAdministrator;
        private PathManager _pathManager;
        private ReadOnlyCollection<string> _defaultPaths;

        public event EventHandler PathChanged;
        public event EventHandler PathSaved;

        public EasyPathControl()
        {
            InitializeComponent();
            _isRunningAsAdministrator = Is.RunningAsAdministrator;
        }

        public EnvironmentVariableTarget Target { get; set; }

        public void Populate()
        {
            _pathManager = new PathManager(Target);
            _pathManager.SetAsDataSource(_listBox);

            IEnumerable<string> defaultPaths = _pathManager.GetPaths();
            _defaultPaths = new List<string>(defaultPaths).AsReadOnly();

            AddPaths(_defaultPaths);
        }

        private AddPathResult TryAddPath(string path)
        {
            if (Directory.Exists(path))
            {
                if (_pathManager.AddPath(path))
                {
                    return AddPathResult.Success;
                }
                else
                {
                    return AddPathResult.DuplicatePath;
                }
            }
            else
            {
                return AddPathResult.InvalidPath;
            }
        }

        protected void OnPathChanged()
        {
            EventHandler handler = PathChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

        protected void OnPathSaved()
        {
            EventHandler handler = PathSaved;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }

        private bool AttemptToAddPath(string path)
        {
            switch (TryAddPath(path))
            {
                case AddPathResult.DuplicatePath:
                    MessageBox.Show("That path is already a part of the PATH environment variable.", "Duplicate Path", MessageBoxButtons.OK);
                    return false;
                case AddPathResult.InvalidPath:
                    MessageBox.Show("That is not a valid directory path.", "Invalid Path", MessageBoxButtons.OK);
                    return false;
                default:
                case AddPathResult.Success:
                    OnPathChanged();
                    return true;
            }
        }

        private void AddPaths(IEnumerable<string> paths)
        {
            try
            {
                _listBox.BeginUpdate();

                if (paths != null)
                {
                    bool pathChanged = false;
                    foreach (string path in paths)
                    {
                        if (TryAddPath(path) == AddPathResult.Success)
                        {
                            pathChanged = true;
                        }
                    }

                    if (pathChanged)
                    {
                        OnPathChanged();
                    }
                }
            }
            finally
            {
                _listBox.EndUpdate();
            }
        }

        private void _textBox_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)
            {
                string potentialPath = _textBox.Text;
                if (AttemptToAddPath(potentialPath))
                {
                    _textBox.Clear();
                }
            }
        }

        private void _textBox_Leave(object sender, System.EventArgs e)
        {
            if (!_textBox.IsEmpty)
            {
                _textBox.ForeColor = Directory.Exists(_textBox.Text) ? TextBox.DefaultForeColor : Color.Red;
            }
        }

        private void _listBox_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete || e.KeyCode == Keys.Back)
            {
                int selectedIndex = _listBox.SelectedIndex;
                if (selectedIndex != -1)
                {
                    string path = _listBox.Items[selectedIndex].ToString();
                    if (_pathManager.RemovePath(path))
                    {
                        OnPathChanged();
                    }
                }
            }
        }

        private void _sortButton_Click(object sender, System.EventArgs e)
        {
            _pathManager.SortPaths();
            OnPathChanged();
        }

        private void _reverseButton_Click(object sender, System.EventArgs e)
        {
            _pathManager.ReversePaths();
            OnPathChanged();
        }

        private void _saveButton_Click(object sender, System.EventArgs e)
        {
            ToggleEnabled(false);
            _savePathBackgroundWorker.RunWorkerAsync();
        }

        private void _resetButton_Click(object sender, System.EventArgs e)
        {
            _pathManager.Reset();
        }

        private void _listBox_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
        }

        private void _listBox_DragDrop(object sender, DragEventArgs e)
        {
            var data = e.Data.GetData(DataFormats.FileDrop) as string[];

            if (data != null && data.Length > 0)
            {
                AddPaths(data);
            }
        }

        private void _savePathBackgroundWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            if (_isRunningAsAdministrator)
            {
                _pathManager.Save();
                e.Result = ExitCode.Success;
            }
            else
            {
                string setPathLocation = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SetPath.exe");
                var processStartInfo = new ProcessStartInfo(setPathLocation, _pathManager.ToString())
                {
                    CreateNoWindow = true,
                    UseShellExecute = true,
                    Verb = "runas"
                };

                using (Process p = Process.Start(processStartInfo))
                {
                    p.WaitForExit();
                    e.Result = (ExitCode)p.ExitCode;
                }
            }
        }

        private void _savePathBackgroundWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            try
            {
                if (e.Error != null)
                {
                    MessageBox.Show("An unexpected error occurred: " + e.Error.Message, "Unexpected Error", MessageBoxButtons.OK);
                    return;
                }
                else if ((ExitCode)e.Result != ExitCode.Success)
                {
                    MessageBox.Show("Unable to save PATH environment variable: " + (e.Result.ToString()));
                    return;
                }

                OnPathSaved();
                MessageBox.Show("Path saved successfully.", "Path Saved", MessageBoxButtons.OK);
            }
            finally
            {
                ToggleEnabled(true);
            }
        }

        private void ToggleEnabled(bool enabled)
        {
            _table.Enabled = enabled;
            _sortButton.Enabled = enabled;
            _reverseButton.Enabled = enabled;
            _resetButton.Enabled = enabled;
            _saveButton.Enabled = enabled;
        }
    }
}
